import { getStorage, showToast, removeStorage } from '@/utils/common';
import { toLang } from '@/utils/lang';

// 拦截器配置
const httpInterceptor = {
    // 拦截前触发
    invoke(options: UniApp.RequestOptions) {
        // 1. 非 http 开头需拼接地址
        if (!options.url.startsWith('http')) {
            options.url = import.meta.env.VITE_API_BASE_URL + options.url;
        }
        // 2. 请求超时
        options.timeout = 1000 * 20;
        // 3. 添加小程序端请求头标识
        options.header = {
            'source-client': 'miniapp',
            ...options.header
        };
        // 4. 添加 token 请求头标识
        const token: string = getStorage('token') || '';
        if (token) {
            options.header.Authorization = token;
        }
    }
};

// 拦截 request 请求
uni.addInterceptor('request', httpInterceptor);
// 拦截 uploadFile 文件上传
uni.addInterceptor('uploadFile', httpInterceptor);

/**
 * 请求函数
 * @param  UniApp.RequestOptions
 * @returns Promise
 *  1. 返回 Promise 对象，用于处理返回值类型
 *  2. 获取数据成功
 *    2.1 提取核心数据 res.data
 *    2.2 添加类型，支持泛型
 *  3. 获取数据失败
 *    3.1 401错误 -> 清理用户信息，跳转到登录页
 *    3.2 其他错误 -> 根据后端错误信息轻提示
 *    3.3 网络错误 -> 提示用户换网络
 */
type Data<T> = {
    data: any;
    code: number;
    message: string;
    result: T;
};
// 2.2 添加类型，支持泛型
const request = <T>(options: UniApp.RequestOptions) => {
    const hideLoading = typeof options.data === 'object' && options.data !== null && (options.data as any).hideLoading;

    if (!hideLoading) {
        uni.showLoading({ title: toLang('common.loading')+'...' });
    }

    // 如果 hideLoading 存在且为 true，则从 data 中移除该属性（防止传给后端）
    if (typeof options.data === 'object' && options.data !== null && (options.data as any).hideLoading !== undefined) {
        delete (options.data as any).hideLoading;
    }

    // 1. 返回 Promise 对象
    return new Promise<Data<T>>((resolve, reject) => {
        uni.request({
            ...options,
            // 响应成功
            success(res) {
                callback(res, resolve, reject);
            },
            // 响应失败
            fail(err) {
                showToast({ title: toLang('common.networkError') });
                reject(err);
            },
            complete() {
                uni.hideLoading();
            }
        });
    });
};

/**
 * 文件上传函数
 * @param options 上传配置，继承自 uni.uploadFile 的配置
 * @returns Promise
 *  1. 返回 Promise 对象，用于处理返回值类型
 *  2. 上传成功
 *    2.1 提取核心数据 res.data
 *    2.2 添加类型，支持泛型
 *  3. 上传失败
 *    3.1 401错误 -> 清理用户信息，跳转到登录页
 *    3.2 其他错误 -> 根据后端错误信息轻提示
 *    3.3 网络错误 -> 提示用户换网络
 */
const uploadFile = <T>(options: UniApp.UploadFileOption) => {
    uni.showLoading({ title: toLang('common.loading')+'...' });
    return new Promise<Data<T>>((resolve, reject) => {
        uni.uploadFile({
            ...options,
            url: options.url || '', // 确保URL存在
            filePath: options.filePath || '', // 确保文件路径存在
            name: options.name || 'file', // 设置默认文件名

            // 上传成功
            success(res) {
                callback(res, resolve, reject);
            },

            // 上传失败
            fail(err) {
                showToast({ title: toLang('common.networkError') });
                reject(err);
            },

            // 完成后隐藏加载提示
            complete() {
                uni.hideLoading();
            }
        });
    });
};

// 修改 callback 参数类型定义，使用 UniApp 的 RequestSuccessCallbackResult
const callback = <T>(res: any, resolve: (arg0: Data<T>) => void, reject: (arg0: any) => void) => {
    let data;
    // 判断 res.data 是否为字符串，再尝试解析 JSON
    try {
        if (typeof res.data === 'string') {
            data = JSON.parse(res.data);
        } else {
            // 如果已经是对象（如小程序中返回的对象），直接使用
            data = res.data as Data<T>;
        }
    } catch (e) {
        data = { code: 500, message: toLang('common.dataFailed'), data: null as any };
    }

    const code = data?.code;
    if (code >= 200 && code < 300) {
        resolve(data);
    } else if (code === 401) {
        showToast({ title: data.message || toLang('common.requestFailure') });
        removeStorage('token');
        removeStorage('user');
        uni.navigateTo({ url: '/pages/login/index' });
        reject(res);
    } else {
        showToast({ title: data.message || toLang('common.requestFailure') });
        reject(res);
    }
};

export { request, uploadFile };
